package dev.ece.suit.skybox;

import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.lwjgl.opengl.GL11;

import dev.ece.core.cells.model.AbstractModel;
import dev.ece.core.shader.TextureCubeShader;
import dev.ece.core.sprite.Sprite;
import dev.ece.core.texture.FBOS;
import dev.ece.core.texture.Texture;
import dev.ece.core.vertex.VAO;
import dev.ece.core.vertex.VBO;
import dev.ece.util.lib.Vector3f;
import dev.ece.util.tools.Camera;
import dev.ece.util.tools.Constants;
import dev.ece.util.tools.transformations.Translation;
/**
 * 天空盒
 * @author 潜行的虚影
 *
 */
public class SkyBox extends Sprite<Object, SkyBoxModel> {

	private final Vector3f position = new Vector3f();
	
	private boolean followCamera;
	
	/**
	 * @param width
	 * @param height
	 * @param texture_paths 纹理顺序：右、左、上、下、后、前
	 */
	public SkyBox(float width, float height, String[] texture_paths) {
		this(width, height, texture_paths, false);
	}
	
	/**
	 * @param width
	 * @param height
	 * @param textures 纹理顺序：右、左、上、下、后、前
	 */
	public SkyBox(float width, float height, Texture textureCube) {
		this(width, height, textureCube, false);
	}
	
	/**
	 * 
	 * @param width
	 * @param height
	 * @param images 纹理顺序：右、左、上、下、后、前
	 */
	public SkyBox(float width, float height, BufferedImage[] images) {
		this(width, height, images, false);
	}
	
	public SkyBox(float width, float height, BufferedImage[] images, boolean followCamera) {
		super(new SkyBoxModel(width, height, images));
		this.followCamera = followCamera;
		this.init();
	}
	
	public SkyBox(float width, float height, String[] texture_paths, boolean followCamera) {
		super(new SkyBoxModel(width, height, texture_paths));
		this.followCamera = followCamera;
		this.init();
	}
	
	public SkyBox(float width, float height, Texture textureCube, boolean followCamera) {
		super(new SkyBoxModel(width, height, textureCube));
		this.followCamera = followCamera;
		this.init();
	}

	private void init() {
		this.transformations.add(new Translation(position));
	}
	
	@Override
	public void onLogic(Camera camera) {
		if(this.isFollowCamera()) {
			position.setX(camera.position.getX());
			position.setZ(camera.position.getZ());
		}
	}
	
	public boolean isFollowCamera() {
		return followCamera;
	}

	public void setFollowCamera(boolean followCamera) {
		this.followCamera = followCamera;
	}

	public Vector3f getPosition() {
		return position;
	}
	
	public float getWidth() {
		return this.getModel().getWidth();
	}

	public float getHeight() {
		return this.getModel().getHeight();
	}
	
	public Texture getTextureCube() {
		return this.getModel().getTextureCube();
	}

}
class SkyBoxModel extends AbstractModel<Object> {
	
	private TextureCubeShader textureCubeShader;
	
	private float width;
	
	private float height;

	private String[] texture_paths;
	
	private BufferedImage[] images;
	
	private Texture textureCube;
	
	private Vector3f[][] vertice;
	
	private VAO vao;
	
	private VBO vbo;
	
	public SkyBoxModel(float width, float height, BufferedImage[] images) {
		this.width = width;
		this.height = height;
		this.images = images;
		this.init();
	}
	
	public SkyBoxModel(float width, float height, String[] texture_paths) {
		this.width = width;
		this.height = height;
		this.texture_paths = texture_paths;
		this.init();
	}
	
	public SkyBoxModel(float width, float height, Texture textureCube) {
		this.width = width;
		this.height = height;
		this.textureCube = textureCube;
		this.init();
	}
	
	
	private void init() {
		float halfWidth = width / 2;
		float halfHeight = height / 2;
		vertice = new Vector3f[6][6];
		//右
		vertice[0] = new Vector3f[] {
			new Vector3f(halfWidth, halfHeight, -halfWidth),
			new Vector3f(halfWidth, -halfHeight, -halfWidth),
			new Vector3f(halfWidth, halfHeight, halfWidth),
			new Vector3f(halfWidth, halfHeight, halfWidth),
			new Vector3f(halfWidth, -halfHeight, -halfWidth),
			new Vector3f(halfWidth, -halfHeight, halfWidth)
		};
		//左
		vertice[1] = new Vector3f[] {
			new Vector3f(-halfWidth, halfHeight, halfWidth),
			new Vector3f(-halfWidth, -halfHeight, halfWidth),
			new Vector3f(-halfWidth, halfHeight, -halfWidth),
			new Vector3f(-halfWidth, halfHeight, -halfWidth),
			new Vector3f(-halfWidth, -halfHeight, halfWidth),
			new Vector3f(-halfWidth, -halfHeight, -halfWidth)
		};
		//顶
		vertice[2] = new Vector3f[] {
			new Vector3f(halfWidth, halfHeight, -halfWidth),
			new Vector3f(halfWidth, halfHeight, halfWidth),
			new Vector3f(-halfWidth, halfHeight, -halfWidth),
			new Vector3f(-halfWidth, halfHeight, -halfWidth),
			new Vector3f(halfWidth, halfHeight, halfWidth),
			new Vector3f(-halfWidth, halfHeight, halfWidth)
		};
		
		//底
		vertice[3] = new Vector3f[] {
			new Vector3f(halfWidth, -halfHeight, halfWidth),
			new Vector3f(halfWidth, -halfHeight, -halfWidth),
			new Vector3f(-halfWidth, -halfHeight, halfWidth),
			new Vector3f(-halfWidth, -halfHeight, halfWidth),
			new Vector3f(halfWidth, -halfHeight, -halfWidth),
			new Vector3f(-halfWidth, -halfHeight, -halfWidth)
		};
		
		//后
		vertice[4] = new Vector3f[] {
			new Vector3f(halfWidth, halfHeight, halfWidth),
			new Vector3f(halfWidth, -halfHeight, halfWidth),
			new Vector3f(-halfWidth, halfHeight, halfWidth),
			new Vector3f(-halfWidth, halfHeight, halfWidth),
			new Vector3f(halfWidth, -halfHeight, halfWidth),
			new Vector3f(-halfWidth, -halfHeight, halfWidth)
		};
		
		//前
		vertice[5] = new Vector3f[] {
			new Vector3f(-halfWidth, halfHeight, -halfWidth),
			new Vector3f(-halfWidth, -halfHeight, -halfWidth),
			new Vector3f(halfWidth, halfHeight, -halfWidth),
			new Vector3f(halfWidth, halfHeight, -halfWidth),
			new Vector3f(-halfWidth, -halfHeight, -halfWidth),
			new Vector3f(halfWidth, -halfHeight, -halfWidth)
		};
		
		setup();
	}
	
	@Override
	public void onRenderDepthMap(Camera camera, Object value) {
	}

	public void setup() {
		textureCubeShader = TextureCubeShader.getDefaultTextureCubeShader();
		if(textureCube == null) {
			if(this.images != null) {
				textureCube = Texture.newCubeInstance(images);
			} else {
				File[] files = new File[this.texture_paths.length];
				for(int i=0; i<this.texture_paths.length; i++) {
					files[i] = Constants.getGameFile(this.texture_paths[i]);
				}
				textureCube = Texture.newCubeInstance(files);
			}
		}
		List<Vector3f> vertice = new ArrayList<Vector3f>();
		for(Vector3f[] row: this.vertice) {
			for(Vector3f col: row) {
				vertice.add(col);
			}
		}
		vbo = VBO.newInstance3f(vertice);
		VBO.unbind();
	}

	@Override
	public void onInitialize() {
		vao = new VAO();
		vao.bind();
		vao.bindVertexArrayBuffer(vbo, 3);
		vao.finish();
		vbo = null;
	}

	@Override
	public void onRender(Camera camera, Object value) {
		textureCubeShader.glUseProgram();
		textureCubeShader.glModelViewProjectMatrix();
		textureCubeShader.glTexture(textureCube);
		vao.bind();
		GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, 36);
	}

	@Override
	public void onRenderAlpha(FBOS defer, Camera camera, Object value) {}

	@Override
	public void destroy() {
		if(textureCube != null) {
			textureCube.destroy();
		}
		if(vao != null) {
			vao.destroy();
		}
		texture_paths = null;
		textureCube = null;
		vertice = null;
		vao = null;
		vbo = null;
	}

	public float getWidth() {
		return width;
	}

	public float getHeight() {
		return height;
	}

	public Texture getTextureCube() {
		return textureCube;
	}

}